package com.gmcloud.common.log.util;

import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import io.swagger.v3.oas.annotations.media.Schema;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.mybatis.spring.SqlSessionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.text.DateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class DataLogUtils {

    private static final Logger logger = LoggerFactory.getLogger(DataLogUtils.class);

    /**
     * 获取SQL语句
     */
    public static String getSql(Configuration configuration, BoundSql boundSql) {
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        String sql = boundSql.getSql().replaceAll("\\s+", " ");
        if (parameterMappings.isEmpty() && parameterObject == null) {
            return sql;
        }
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            sql = sql.replaceFirst("\\?", getParameterValue(parameterObject));
        } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            for (ParameterMapping parameterMapping : parameterMappings) {
                String propertyName = parameterMapping.getProperty();
                if (metaObject.hasGetter(propertyName)) {
                    Object obj = metaObject.getValue(propertyName);
                    sql = sql.replaceFirst("\\?", getParameterValue(obj));
                } else if (boundSql.hasAdditionalParameter(propertyName)) {
                    Object obj = boundSql.getAdditionalParameter(propertyName);
                    sql = sql.replaceFirst("\\?", getParameterValue(obj));
                }
            }
        }
        return sql;
    }

    /**
     * 获取参数值
     */
    public static String getParameterValue(Object o) {
        if (o == null) {
            return "";
        }
        if (o instanceof String s) {
            return "'" + s + "'";
        }
        if (o instanceof Date date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
            return "'" + formatter.format(date) + "'";
        }
        if (o instanceof LocalDateTime dateTime) {
            DateTimeFormatter dfDate = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            return "'" + dateTime.format(dfDate) + "'";
        }
        return o.toString();
    }

    /**
     * 获取调用的类和方法名称
     */
    public static String[] getClassAndMethod() {
        String[] result = new String[]{"", ""};
        StackTraceElement[] stackTraceElementArray = Thread.currentThread().getStackTrace();
        int n = stackTraceElementArray.length;
        for (int i = n - 1; i >= 0; i--) {
            String className = stackTraceElementArray[i].getClassName();
            if (className.contains(".service.")) {
                result[0] = getSimpleClassName(className);
                result[1] = stackTraceElementArray[i].getMethodName();
                break;
            } else if (className.contains(".controller.")) {
                result[0] = getSimpleClassName(className);
                result[1] = stackTraceElementArray[i].getMethodName();
            }
        }
        return result;
    }

    /**
     * 根据类全称获取简单名称
     */
    public static String getSimpleClassName(String className) {
        className = StringUtils.split(className, "$$")[0];
        int index = className.lastIndexOf(".");
        if (index != -1) {
            return className.substring(index + 1);
        }
        return className;
    }

    /**
     * 获取两个相同实体对象不同属性值
     */
    public static DataCompareResult getDiffValue(Object object1, Object object2) {

        DataCompareResult dataCompareResult = new DataCompareResult();

        // 对象都为null，返回空数据
        if (object1 == null && object2 == null) {
            return dataCompareResult;
        }

        Long id = null;
        LinkedHashMap<String, Object[]> dataChange = new LinkedHashMap<>();
        if (object1 == null) {// 旧对象为null
            Field[] object2Fields = object2.getClass().getDeclaredFields();
            for (int i = 0; i < object2Fields.length; i++) {
                ReflectionUtils.makeAccessible(object2Fields[i]);
                Field field = object2Fields[i];
                try {
                    Object value2 = object2Fields[i].get(object2);
                    // 忽略表不存在的字段
                    if (field.isAnnotationPresent(TableField.class) && !field.getAnnotation(TableField.class).exist()) {
                        continue;
                    }
                    // 获取id主键值
                    if ("id".equals(field.getName())) {
                        id = Long.parseLong(value2.toString());
                    }
                    if (field.isAnnotationPresent(Schema.class) && StringUtils.isNotBlank(field.getAnnotation(Schema.class).title())){
                        dataChange.put(field.getAnnotation(Schema.class).description(), new Object[]{"", value2});
                    }else {
                        dataChange.put(field.getName(), new Object[]{"", value2});
                    }
                } catch (IllegalAccessException e) {
                    logger.error("非法操作", e);
                }
            }
        } else if (object2 == null) {// 新对象为null
            Field[] object1Fields = object1.getClass().getDeclaredFields();
            for (Field object1Field : object1Fields) {
                ReflectionUtils.makeAccessible(object1Field);
                try {
                    Object value1 = object1Field.get(object1);
                    // 忽略表不存在的字段
                    if (object1Field.isAnnotationPresent(TableField.class) && !object1Field.getAnnotation(TableField.class).exist()) {
                        continue;
                    }
                    // 获取id主键值
                    if ("id".equals(object1Field.getName()) || object1Field.isAnnotationPresent(TableId.class)) {
                        id = Long.parseLong(value1.toString());
                    }
                    if (object1Field.isAnnotationPresent(Schema.class) && StringUtils.isNotBlank(object1Field.getAnnotation(Schema.class).title())){
                        dataChange.put(object1Field.getAnnotation(Schema.class).description(), new Object[]{value1, ""});
                    }else {
                        dataChange.put(object1Field.getName(), new Object[]{value1, ""});
                    }
                } catch (IllegalAccessException e) {
                    logger.error("非法操作", e);
                }
            }
        } else {// 旧对象和新对象都不为null
            Field[] object1Fields = object1.getClass().getDeclaredFields();
            Field[] object2Fields = object2.getClass().getDeclaredFields();
            for (int i = 0; i < object1Fields.length; i++) {
                ReflectionUtils.makeAccessible(object1Fields[i]);
                ReflectionUtils.makeAccessible(object2Fields[i]);
                Field field = object1Fields[i];

                try {
                    Object value1 = object1Fields[i].get(object1);
                    Object value2 = object2Fields[i].get(object2);
                    // 忽略表不存在的字段
                    if (field.isAnnotationPresent(TableField.class) && !field.getAnnotation(TableField.class).exist()) {
                        continue;
                    }
                    // 获取id主键值
                    if ("id".equals(field.getName())) {
                        id = Long.parseLong(value1.toString());
                    }
                    // 新值为null处理
                    if (value2 == null && (!field.isAnnotationPresent(TableField.class) || !field.getAnnotation(TableField.class).updateStrategy().equals(FieldStrategy.IGNORED))) {
                            continue;

                    }
                    if (!Objects.equals(value1, value2)) {
                        if (field.isAnnotationPresent(Schema.class) && StringUtils.isNotBlank(field.getAnnotation(Schema.class).description())){
                            dataChange.put(field.getAnnotation(Schema.class).description(), new Object[]{value1, value2});
                        }else {
                            dataChange.put(field.getName(), new Object[]{value1, value2});
                        }
                    }
                } catch (IllegalAccessException e) {
                    logger.error("非法操作", e);
                }
            }
        }

        // 返回数据
        dataCompareResult.setId(id);
        dataCompareResult.setDataChange(dataChange);

        return dataCompareResult;

    }

    /**
     * 查询旧数据(只获取第一条数据)
     */
    public static Object selectOldData(String sql, TableInfo tableInfo) {
        String selectSql = "AND " + sql.substring(sql.toUpperCase().lastIndexOf("WHERE") + 5);
        Map<String, Object> map = new HashMap<>(1);
        map.put(Constants.WRAPPER, Wrappers.query().eq("1", 1).last(selectSql));
        SqlSessionFactory sqlSessionFactory = SqlHelper.sqlSessionFactory(tableInfo.getEntityType());
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<?> oldData;
        try {
            // tableInfo.getSqlStatement(SqlMethod.SELECT_LIST.getMethod())
            oldData = sqlSession.selectList(tableInfo.getSqlStatement(SqlMethod.SELECT_LIST.getMethod()), map);
        } finally {
            SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
        }
        return oldData != null && !oldData.isEmpty() ? oldData.get(0) : null;
    }

    /**
     * 比较修改前后数据
     */
    public static DataCompareResult getDataCompare(SqlCommandType sqlCommandType, String sql, TableInfo tableInfo, Object entity) {
        DataCompareResult dataCompareResult = new DataCompareResult();
        if (SqlCommandType.INSERT.equals(sqlCommandType)) {// 新增
            dataCompareResult = DataLogUtils.getDiffValue(null, entity);
        } else if (SqlCommandType.UPDATE.equals(sqlCommandType)) {// 修改
            dataCompareResult = DataLogUtils.getDiffValue(selectOldData(sql, tableInfo), entity);
        } else if (SqlCommandType.DELETE.equals(sqlCommandType)) {// 删除
            dataCompareResult = DataLogUtils.getDiffValue(selectOldData(sql, tableInfo), null);
        }
        return dataCompareResult;
    }

}
